﻿using System.Data;

namespace QueryBuilder.Core.Query.SqlTypes
{
    /// <summary>
    /// 
    /// </summary>
    public class SqlType
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="SqlType"/> class.
        /// </summary>
        /// <param name="dbType">Type of the db.</param>
        public SqlType(DbType dbType)
        {
            this.DbType = dbType;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SqlType"/> class.
        /// </summary>
        /// <param name="dbType">Type of the db.</param>
        /// <param name="length">The length.</param>
        public SqlType(DbType dbType, int length) : this(dbType)
        {
            this.Length = length;
            this.LengthDefined = true;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SqlType"/> class.
        /// </summary>
        /// <param name="dbType">Type of the db.</param>
        /// <param name="precision">The precision.</param>
        /// <param name="scale">The scale.</param>
        public SqlType(DbType dbType, byte precision, byte scale) : this(dbType)
        {
            this.Precision = precision;
            this.Scale = scale;
            this.PrecisionDefined = true;
        }

        /// <summary>
        /// Gets or sets the type of the db.
        /// </summary>
        /// <value>The type of the db.</value>
        public DbType DbType { get; set; }

        /// <summary>
        /// Gets or sets the length.
        /// </summary>
        /// <value>The length.</value>
        public int Length { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether [length defined].
        /// </summary>
        /// <value><c>true</c> if [length defined]; otherwise, <c>false</c>.</value>
        public bool LengthDefined { get; private set; }

        /// <summary>
        /// Gets or sets the precision.
        /// </summary>
        /// <value>The precision.</value>
        public byte Precision { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether [precision defined].
        /// </summary>
        /// <value><c>true</c> if [precision defined]; otherwise, <c>false</c>.</value>
        public bool PrecisionDefined { get; private set; }

        /// <summary>
        /// Gets or sets the scale.
        /// </summary>
        /// <value>The scale.</value>
        public byte Scale { get; private set; }

        /// <summary>
        /// Equalses the specified RHS SQL type.
        /// </summary>
        /// <param name="rhsSqlType">Type of the RHS SQL.</param>
        /// <returns></returns>
        public bool Equals(SqlType rhsSqlType)
        {
            if (rhsSqlType == null)
            {
                return false;
            }
            if (this.LengthDefined)
            {
                return (this.DbType.Equals(rhsSqlType.DbType) && (this.Length == rhsSqlType.Length));
            }
            if (!this.PrecisionDefined)
            {
                return this.DbType.Equals(rhsSqlType.DbType);
            }
            return ((this.DbType.Equals(rhsSqlType.DbType) && (this.Precision == rhsSqlType.Precision)) &&
                    (this.Scale == rhsSqlType.Scale));
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        /// 	<c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            if (obj != this)
            {
                return this.Equals(obj as SqlType);
            }
            return true;
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            if (this.LengthDefined)
            {
                return ((this.DbType.GetHashCode()/2) + (this.Length.GetHashCode()/2));
            }
            if (this.PrecisionDefined)
            {
                return (((this.DbType.GetHashCode()/3) + (this.Precision.GetHashCode()/3)) +
                        (this.Scale.GetHashCode()/3));
            }
            return this.DbType.GetHashCode();
        }
    }
}